<template>
  <view
    class="navbar"
    :class="[{ 'navbar--mp': isMp, 'navbar--black-icon': blackIcon, 'navbar--immersive': immersive && opacity < 1 }]"
  >
    <view :class="[{ 'navbar--fixed': fixed }]" :style="[getStyle]">
      <view v-if="fixed" :style="{ height: statusBarHeight + 'px' }"></view>
      <view class="navbar__wrap">
        <view class="navbar__left">
          <view v-if="getMore && isMp" class="navbar__left__btn-group">
            <view v-if="back" class="icon-btn" @click.stop>
              <m-icon :name="backIcon" size="40" :color="color" @click="handleClick('back')" />
            </view>
            <view class="icon-btn">
              <m-popover :list="more" @choose="handleMoreChoose">
                <m-icon
                  slot="reference"
                  name="more-horizontal"
                  size="40"
                  :color="color"
                  @click="handleClick('right', 'more')"
                />
              </m-popover>
            </view>
          </view>
          <view v-else-if="back" class="icon-bg">
            <m-icon
              :name="backIcon"
              size="40"
              :color="blackIcon && immersive && opacity < 1 ? '#fff' : color"
              @click="handleClick('back')"
            />
          </view>
          <view v-if="$slots.backText || $scopedSlots.backText || backText">
            <slot v-if="$slots.backText || $scopedSlots.backText" name="backText" :args="args" />
            <template v-else>
              {{ backText }}
            </template>
          </view>
          <view v-if="backImg">
            <slot name="leftImg" />
          </view>
        </view>
        <view class="navbar__center" :style="{ opacity: immersive ? opacity : 1 }">
          <slot name="center" :args="args" />
        </view>
        <view class="navbar__right" :style="[getRightStyle]">
          <template v-if="!(isMp && fixed)">
            <slot v-if="$scopedSlots.right || $slots.right" name="right" :args="args" />
            <template v-else-if="getRight">
              <view v-for="(rightIcon, rightIconIndex) in getRight" :key="rightIconIndex" class="navbar__right__icon">
                <view v-if="rightIcon === 'more'">
                  <m-popover :list="more" placement="bottom-end" @choose="handleMoreChoose">
                    <view slot="reference" class="icon-bg">
                      <m-icon
                        name="more"
                        size="40"
                        :color="blackIcon && immersive && opacity < 1 ? '#fff' : color"
                        @click="handleClick('right', 'more')"
                      />
                    </view>
                  </m-popover>
                </view>
                <view v-else-if="/^[a-zA-Z\-]*$/.test(rightIcon)" class="icon-bg">
                  <m-icon
                    :name="rightIcon"
                    size="40"
                    :color="blackIcon && immersive && opacity < 1 ? '#fff' : color"
                    @click="handleClick('right', rightIcon)"
                  />
                </view>
                <m-button v-else type="text" :color="color" size="lg" @click="handleClick('right', rightIcon)">
                  {{ rightIcon }}
                </m-button>
              </view>
            </template>
          </template>
        </view>
        <view
          v-if="$slots.title || $scopedSlots.title || title"
          class="navbar__title"
          :style="{ opacity: immersive ? titleOpacity : 1, transition: '0.3s' }"
          @click="handleClick('title')"
        >
          <slot v-if="$slots.title || $scopedSlots.title" name="title" :args="args" />
          <template v-else>
            {{ title }}
          </template>
        </view>
      </view>
      <view
        v-if="fixed && ((mpDescShow && ($slots.mpDesc || $scopedSlots.mpDesc || mpDesc)) || getMpRight.length)"
        class="navbar__mp"
      >
        <view v-if="mpDescShow" class="navbar__mp__left" @click="handleClickMpDesc">
          <slot v-if="$slots.mpDesc || $scopedSlots.mpDesc" name="mpDesc" :args="args" />
          <template v-else>
            {{ mpDesc }}
          </template>
        </view>
        <view v-if="$slots.right || $scopedSlots.right || getMpRight" class="navbar__mp__right">
          <slot v-if="$slots.right || $scopedSlots.right" name="right" :args="args" />
          <template v-else-if="getMpRight">
            <view v-for="(rightIcon, rightIconIndex) in getMpRight" :key="rightIconIndex">
              <m-icon
                v-if="/^[a-zA-Z\-]*$/.test(rightIcon)"
                :name="rightIcon"
                size="40"
                :color="blackIcon && immersive && opacity < 1 ? '#fff' : color"
                @click="handleClick('right', rightIcon)"
              />
              <m-button v-else type="text" :color="color" @click="handleClick('right', rightIcon)">{{
                rightIcon
              }}</m-button>
            </view>
          </template>
        </view>
      </view>
    </view>

    <!-- 解决fixed定位后导航栏塌陷的问题 -->
    <view
      v-if="fixed && disappear"
      class="u-navbar-placeholder"
      :style="{
        width: '100%',
        height: getNavHeight + 'px',
      }"
    />
  </view>
</template>

<script lang="ts">
import {
  computed,
  defineComponent,
  reactive,
  toRefs,
  PropType,
  ref,
  inject,
  onMounted,
  onUnmounted,
} from '@vue/composition-api'
import { isMp } from '@/utils/platform'
import { useRouter, usePageScroll } from '@/hooks'
import addColor from '@/utils/add-color'

export default defineComponent({
  props: {
    back: {
      type: Boolean,
      default: true,
    },
    // 点击后跳转的目标路由对象，同 vue-router 的 to 属性 属性
    to: {
      type: Object,
      default: null,
    },
    // 是否在跳转时替换当前页面历史
    replace: {
      type: Boolean,
      default: false,
    },
    title: {
      type: String,
      default: '',
    },
    right: {
      type: Array as PropType<string[]>,
      default: () => [],
    },
    fixed: {
      type: Boolean,
      default: true,
    },
    disappear: {
      type: Boolean,
      default: true,
    },
    background: {
      type: String,
      default: '#f5f5f5',
    },
    backgroundTransparent: {
      type: Boolean,
      default: false,
    },
    // 透明，就是沉浸式
    immersive: {
      type: Boolean,
      default: false,
    },
    blackIcon: {
      type: Boolean,
      default: false,
    },
    color: {
      type: String,
      default: '#262626',
    },
    mpDesc: {
      type: String,
      default: '',
    },
    mpDescShow: {
      type: Boolean,
      default: true,
    },
    args: {
      type: [Array, Object],
      default: () => ({}),
    },
    // 自定义返回逻辑
    // eslint-disable-next-line vue/require-default-prop
    customBack: Function,
    backIcon: {
      type: String,
      default: 'back',
    },
    backText: {
      type: String,
      default: '',
    },
    backImg: {
      type: Boolean,
      default: false,
    },
    zIndex: {
      type: Number,
      default: 980,
    },
    more: {
      type: Array,
      default: () => [],
    },
    autoBack: {
      type: Boolean,
      default: true,
    },
  },
  emits: ['back', 'more', 'title', 'right', 'mpDesc', 'init'],
  setup(props, { emit, slots }) {
    const router: any = useRouter()
    // 获取系统状态栏的高度
    let systemInfo = uni.getSystemInfoSync()
    let menuButtonInfo: any = {}
    // 如果是小程序，获取右上角胶囊的尺寸信息，避免导航栏右侧内容与胶囊重叠(支付宝小程序非本API，尚未兼容)
    // #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ
    menuButtonInfo = uni.getMenuButtonBoundingClientRect()
    // #endif
    const windowWidth = systemInfo.windowWidth
    const state = reactive({
      isMp,
      statusBarHeight: systemInfo.statusBarHeight || 0,
    })
    const getNavHeight = computed(() => {
      if (props.immersive) {
        return state.statusBarHeight
      }

      return (
        state.statusBarHeight +
        Number(getNavbarHeight.value) +
        (props.mpDescShow && (slots.mpDesc || props.mpDesc || getMpRight.value.length) ? 32 : 0)
      )
    })
    const getRight = computed(() => {
      const { right } = props
      if (isMp) {
        return right.filter((x) => !['share', 'more'].includes(x))
      }
      return right
    })
    const getMpRight = computed(() => {
      const { right } = props
      if (isMp) {
        return right.filter((x) => !['share', 'more'].includes(x)).filter((x) => x)
      }

      return []
    })
    const getMore = computed(() => {
      const { right } = props
      return right.find((x) => x === 'more')
    })
    const getRightStyle = computed(() => {
      const { fixed } = props
      if (isMp && fixed) {
        return {
          width: menuButtonInfo.width + (windowWidth - menuButtonInfo.right) + 'px',
        }
      }

      return {}
    })
    const getNavbarHeight = computed(() => {
      if (isMp) {
        return systemInfo.platform == 'ios' ? 44 : 48
      }
      return 44
    })
    const handleClick = (type, subType = '') => {
      const { to, replace, autoBack } = props
      emit(type, subType)
      if (type === 'back' && autoBack) {
        if (to) {
          const routerName = to && (to.name as string)
          if (['Index', 'Category', 'Community', 'Example', 'Mine', 'Cart'].includes(routerName)) {
            router.pushTab(to)
          } else {
            router[replace ? 'replace' : 'push'](to)
          }

          return
        }
        try {
          // #ifdef H5
          uni.navigateBack({
            delta: 1,
          })
          // #endif

          // #ifndef H5
          const pages = getCurrentPages()
          if (pages.length > 1) {
            uni.navigateBack({
              delta: 1,
            })
          } else {
            router.pushTab('Index')
          }
          // #endif
        } catch (ex) {
          router.pushTab('Index')
        }
      } else {
        ;(props as any).customBack()
      }
    }
    const opacity = ref(props.immersive || props.backgroundTransparent ? 0 : 1)
    const titleOpacity = ref(props.immersive || props.backgroundTransparent ? 0 : 1)
    const getStyle = computed(() => {
      if (props.immersive && opacity.value < 1) {
        return { background: 'transparent', color: addColor(props.color), zIndex: props.zIndex }
      }

      if (props.backgroundTransparent) {
        const bgColor = props.background.length === 4 ? props.background + props.background.slice(1) : props.background
        const r = parseInt('0x' + bgColor.slice(1, 3))
        const g = parseInt('0x' + bgColor.slice(3, 5))
        const b = parseInt('0x' + bgColor.slice(5, 7))

        const background = `rgba(${r}, ${g}, ${b}, ${opacity.value})`
        return { background, color: addColor(props.color), zIndex: props.zIndex }
      }

      return { background: addColor(props.background), color: addColor(props.color), zIndex: props.zIndex }
    })
    const handleMoreChoose = (item, index) => {
      emit('more', item, index)
    }
    const windowTop: any = inject('windowTop')

    onMounted(() => {
      if (props.fixed && windowTop) {
        windowTop.value = getNavHeight?.value
      }
      emit('init', { height: getNavHeight?.value })

      // #ifdef H5
      // set page title
      if (props.fixed && props.title) {
        document.title = props.title
      }
      // #endif
    })

    onUnmounted(() => {
      if (props.fixed) {
        windowTop.value = state.statusBarHeight + Number(getNavbarHeight.value)
      }
    })

    usePageScroll(({ scrollTop }) => {
      if (!(props.immersive || props.backgroundTransparent)) {
        return
      }

      if (scrollTop > 0) {
        if (scrollTop > 50) {
          opacity.value = 1
          titleOpacity.value = 1
        } else {
          opacity.value = scrollTop / 50
          titleOpacity.value = 0
        }
      } else {
        opacity.value = 0
      }
    })
    const handleClickMpDesc = () => {
      emit('mpDesc')
    }
    return {
      ...toRefs(state),
      getRight,
      getMore,
      getRightStyle,
      getMpRight,
      getNavbarHeight,
      handleClick,
      opacity,
      titleOpacity,
      getStyle,
      handleMoreChoose,
      getNavHeight,
      handleClickMpDesc,
    }
  },
})
</script>
<style lang="scss" scoped>
.navbar {
  &__wrap {
    display: flex;
    align-items: center;
    position: relative;
    height: 88rpx;
    position: relative;
  }
  &__left {
    font-size: 28rpx;
    display: flex;
    align-items: center;
    cursor: pointer;
    padding-left: 24rpx;
    &__btn-group {
      margin-left: -8rpx;
      padding: 0 12rpx;
      background: $--bg-content-c;
      border-radius: 32rpx;
      border: 1rpx solid #ebebeb;
      display: flex;
    }
    &__back-text {
      font-weight: bold;
      padding-left: 8rpx;
    }
  }
  .icon-btn {
    padding: 8rpx 11rpx;
    position: relative;
  }
  &__center {
    flex: 1;
    min-width: 0;
  }
  &__title {
    font-size: 32rpx;
    font-weight: bold;
    width: 53%;
    min-width: 53%;
    max-width: 70%;
    margin: 0 auto;
    text-align: center;
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    z-index: 1;
    @include single-line-text-overflow;
  }
  &__right {
    font-size: 28rpx;
    display: flex;
    align-items: center;
    padding-right: 24rpx;
    cursor: pointer;
    // & > view {
    //   margin-left: 8rpx;
    //   &:first-child {
    //     margin-left: 0;
    //   }
    // }

    .navbar__right__icon {
      margin-left: 24rpx;
    }
  }
  &__mp {
    height: 64rpx;
    display: flex;
    padding: 0 24rpx;
    justify-content: space-between;
    align-items: center;
    &__left {
      min-width: 0;
      flex: 1;
    }
    &__right {
      display: flex;
      align-items: center;
    }
  }
  &--fixed {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 2;
    transition: background-color 0.3s ease-in-out;
    border-bottom: 1rpx solid #dfe5eb;
  }
  &--immersive.navbar--black-icon {
    .navbar__right__icon {
      margin-left: 40rpx;
    }
    .icon-bg {
      position: relative;
      &::before {
        content: '';
        position: absolute;
        top: 0;
        left: 0;
        top: 50%;
        left: 50%;
        transform: translateY(-50%) translateX(-50%);
        border-radius: 100%;
        width: 64rpx;
        height: 64rpx;
        background: rgba(0, 0, 0, 0.4);
        z-index: -1;
      }
    }
  }
}
</style>
